{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Classifying Images with pre-built TF Container on Vertex AI\n",
    "\n",
    "## Introduction\n",
    "\n",
    "In this notebook, you learn how to implement different image models on MNIST using the [tf.keras API](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras).\n",
    "\n",
    "## Learning objectives\n",
    "1. Understand how to build a Dense Neural Network (DNN) for image classification.\n",
    "2. Understand how to use dropout (DNN) for image classification.\n",
    "3. Understand how to use Convolutional Neural Networks (CNN).\n",
    "4. Know how to deploy and use an image classifcation model using Google Cloud's [Vertex AI](https://cloud.google.com/vertex-ai/).\n",
    "\n",
    "Each learning objective will correspond to a __#TODO__ in the [student lab notebook](../labs/classifying_images_with_pre-built_tf_container_on_vertex_ai.ipynb) -- try to complete that notebook first before reviewing this solution notebook.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Configuring the parameters\n",
    "\n",
    "First, configure the parameters below to match your own Google Cloud project details.\n",
    "If you don’t want to change the region, leave all settings as they are. Otherwise, update the region as you want, leave other settings as they are and run the cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from datetime import datetime\n",
    "import os\n",
    "\n",
    "REGION = 'Region' #specify the default region of your lab \n",
    "PROJECT = !(gcloud config get-value core/project)\n",
    "PROJECT = PROJECT[0]\n",
    "BUCKET =  PROJECT\n",
    "MODEL_TYPE = \"cnn\"  # \"linear\", \"dnn\", \"dnn_dropout\", or \"cnn\"\n",
    "\n",
    "# Do not change these\n",
    "os.environ[\"PROJECT\"] = PROJECT\n",
    "os.environ[\"BUCKET\"] = BUCKET\n",
    "os.environ[\"REGION\"] = REGION\n",
    "os.environ[\"MODEL_TYPE\"] = MODEL_TYPE"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Building a dynamic model\n",
    "\n",
    "This part of notebook demonstrates how to implement DNN and CNN models on [MNIST](http://yann.lecun.com/exdb/mnist/) using the [tf.keras API](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras). \n",
    "\n",
    "In the previous notebook, “[classifying_images_with_a_nn_and_dnn_model.ipynb](https://github.com/GoogleCloudPlatform/training-data-analyst/blob/master/courses/machine_learning/deepdive2/computer_vision_fun/solutions/classifying_images_with_a_nn_and_dnn_model.ipynb)”, you run the code directly from the notebook. In this notebook, you see that you can also package your notebook as a python module on Vertex AI.\n",
    "\n",
    "The boilerplate structure for this module has already been set up in the folder mnist_models. The module lives in the sub-folder, trainer, and is designated as a python package with the empty __init__.py (mnist_models/trainer/__init__.py) file. It still needs the model and a trainer to run it, so let's make them.\n",
    "\n",
    "Start with the trainer file first. This file parses command line arguments to feed into the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing mnist_models/trainer/task.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile mnist_models/trainer/task.py\n",
    "import argparse\n",
    "import json\n",
    "import os\n",
    "import sys\n",
    "\n",
    "from . import model\n",
    "\n",
    "\n",
    "def _parse_arguments(argv):\n",
    "    \"\"\"Parses command-line arguments.\"\"\"\n",
    "    parser = argparse.ArgumentParser()\n",
    "    parser.add_argument(\n",
    "        '--model_type',\n",
    "        help='Which model type to use',\n",
    "        type=str, default='linear')\n",
    "    parser.add_argument(\n",
    "        '--epochs',\n",
    "        help='The number of epochs to train',\n",
    "        type=int, default=10)\n",
    "    parser.add_argument(\n",
    "        '--steps_per_epoch',\n",
    "        help='The number of steps per epoch to train',\n",
    "        type=int, default=100)\n",
    "    parser.add_argument(\n",
    "        '--job-dir',\n",
    "        help='Directory where to save the given model',\n",
    "        type=str, default='mnist_models/')\n",
    "    return parser.parse_known_args(argv)\n",
    "\n",
    "\n",
    "def main():\n",
    "    \"\"\"Parses command line arguments and kicks off model training.\"\"\"\n",
    "    args = _parse_arguments(sys.argv[1:])[0]\n",
    "\n",
    "    # Configure path for hyperparameter tuning.\n",
    "    trial_id = json.loads(\n",
    "        os.environ.get('TF_CONFIG', '{}')).get('task', {}).get('trial', '')\n",
    "    output_path = args.job_dir if not trial_id else args.job_dir + '/'\n",
    "\n",
    "    model_layers = model.get_layers(args.model_type)\n",
    "    image_model = model.build_model(model_layers, args.job_dir)\n",
    "    model_history = model.train_and_evaluate(\n",
    "        image_model, args.epochs, args.steps_per_epoch, args.job_dir)\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, group non-model functions into a util file to keep the model file simple. Use the scale and load_dataset functions to scale images from a 0-255 int range to a 0-1 float range and load MNIST dataset into a tf.data.Dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing mnist_models/trainer/util.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile mnist_models/trainer/util.py\n",
    "import tensorflow as tf\n",
    "\n",
    "\n",
    "def scale(image, label):\n",
    "    \"\"\"Scales images from a 0-255 int range to a 0-1 float range\"\"\"\n",
    "    image = tf.cast(image, tf.float32)\n",
    "    image /= 255\n",
    "    image = tf.expand_dims(image, -1)\n",
    "    return image, label\n",
    "\n",
    "\n",
    "def load_dataset(\n",
    "        data, training=True, buffer_size=5000, batch_size=100, nclasses=10):\n",
    "    \"\"\"Loads MNIST dataset into a tf.data.Dataset\"\"\"\n",
    "    (x_train, y_train), (x_test, y_test) = data\n",
    "    x = x_train if training else x_test\n",
    "    y = y_train if training else y_test\n",
    "    # One-hot encode the classes\n",
    "    y = tf.keras.utils.to_categorical(y, nclasses)\n",
    "    dataset = tf.data.Dataset.from_tensor_slices((x, y))\n",
    "    dataset = dataset.map(scale).batch(batch_size)\n",
    "    if training:\n",
    "        dataset = dataset.shuffle(buffer_size).repeat()\n",
    "    return dataset\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now you can code the models. The tf.keras API accepts an array of layers into a model object, so you can create a dictionary of layers based on the different model types you want to use. mnist_models/trainer/model.py file has three functions: get_layers, build_model and train_and_evaluate. \n",
    "\n",
    "In get_layers function: Youl build the structure of our model in get_layers with four different layers:\n",
    "\n",
    "* First, you define a linear model.\n",
    "* Second, you define the Keras layers for a DNN model.\n",
    "* Third, you define the Keras layers for a dropout model.\n",
    "* Lastly, you define the Keras layers for a CNN model.\n",
    "\n",
    "In the build_model function: You compile the model, specifying an optimizer to use, the loss to minimize, and metrics to report. \n",
    "\n",
    "Finally in the train_and_evaluate function, you compile your Keras model by loading data into it for training.\n",
    "\n",
    "Note that these models progressively build on each other. Look at the imported tensorflow.keras.layers modules and the default values for the variables defined in get_layers for guidance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing mnist_models/trainer/model.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile mnist_models/trainer/model.py\n",
    "import os\n",
    "import shutil\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "from tensorflow.keras import Sequential\n",
    "from tensorflow.keras.callbacks import TensorBoard\n",
    "from tensorflow.keras.layers import (\n",
    "    Conv2D, Dense, Dropout, Flatten, MaxPooling2D, Softmax)\n",
    "\n",
    "from . import util\n",
    "\n",
    "\n",
    "# Image Variables\n",
    "WIDTH = 28\n",
    "HEIGHT = 28\n",
    "\n",
    "\n",
    "def get_layers(\n",
    "        model_type,\n",
    "        nclasses=10,\n",
    "        hidden_layer_1_neurons=400,\n",
    "        hidden_layer_2_neurons=100,\n",
    "        dropout_rate=0.25,\n",
    "        num_filters_1=64,\n",
    "        kernel_size_1=3,\n",
    "        pooling_size_1=2,\n",
    "        num_filters_2=32,\n",
    "        kernel_size_2=3,\n",
    "        pooling_size_2=2):\n",
    "    \"\"\"Constructs layers for a keras model based on a dict of model types.\"\"\"\n",
    "    model_layers = {\n",
    "        'linear': [\n",
    "            Flatten(),\n",
    "            Dense(nclasses),\n",
    "            Softmax()\n",
    "        ],\n",
    "        # TODO 1: Define the Keras layers for a DNN model\n",
    "        'dnn': [\n",
    "            Flatten(),\n",
    "            Dense(hidden_layer_1_neurons, activation='relu'),\n",
    "            Dense(hidden_layer_2_neurons, activation='relu'),\n",
    "            Dense(nclasses),\n",
    "            Softmax()\n",
    "        ],\n",
    "        # TODO 2: Define the Keras layers for a dropout model\n",
    "        'dnn_dropout': [\n",
    "            Flatten(),\n",
    "            Dense(hidden_layer_1_neurons, activation='relu'),\n",
    "            Dense(hidden_layer_2_neurons, activation='relu'),\n",
    "            Dropout(dropout_rate),\n",
    "            Dense(nclasses),\n",
    "            Softmax()\n",
    "        ],\n",
    "        # TODO 3: Define the Keras layers for a CNN model\n",
    "        'cnn': [\n",
    "            Conv2D(num_filters_1, kernel_size=kernel_size_1,\n",
    "                   activation='relu', input_shape=(WIDTH, HEIGHT, 1)),\n",
    "            MaxPooling2D(pooling_size_1),\n",
    "            Conv2D(num_filters_2, kernel_size=kernel_size_2,\n",
    "                   activation='relu'),\n",
    "            MaxPooling2D(pooling_size_2),\n",
    "            Flatten(),\n",
    "            Dense(hidden_layer_1_neurons, activation='relu'),\n",
    "            Dense(hidden_layer_2_neurons, activation='relu'),\n",
    "            Dropout(dropout_rate),\n",
    "            Dense(nclasses),\n",
    "            Softmax()\n",
    "        ]\n",
    "    }\n",
    "    return model_layers[model_type]\n",
    "\n",
    "\n",
    "def build_model(layers, output_dir):\n",
    "    \"\"\"Compiles keras model for image classification.\"\"\"\n",
    "    model = Sequential(layers)\n",
    "    model.compile(optimizer='adam',\n",
    "                  loss='categorical_crossentropy',\n",
    "                  metrics=['accuracy'])\n",
    "    return model\n",
    "\n",
    "\n",
    "def train_and_evaluate(model, num_epochs, steps_per_epoch, output_dir):\n",
    "    \"\"\"Compiles keras model and loads data into it for training.\"\"\"\n",
    "    mnist = tf.keras.datasets.mnist.load_data()\n",
    "    train_data = util.load_dataset(mnist)\n",
    "    validation_data = util.load_dataset(mnist, training=False)\n",
    "\n",
    "    callbacks = []\n",
    "    if output_dir:\n",
    "        tensorboard_callback = TensorBoard(log_dir=output_dir)\n",
    "        callbacks = [tensorboard_callback]\n",
    "\n",
    "    history = model.fit(\n",
    "        train_data,\n",
    "        validation_data=validation_data,\n",
    "        epochs=num_epochs,\n",
    "        steps_per_epoch=steps_per_epoch,\n",
    "        verbose=2,\n",
    "        callbacks=callbacks)\n",
    "\n",
    "    if output_dir:\n",
    "        export_path = os.path.join(output_dir, 'keras_export')\n",
    "        model.save(export_path, save_format='tf')\n",
    "\n",
    "    return history"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Local Training\n",
    "\n",
    "After completing the set up, you can run locally to test the code. Some of the previous tests have been copied over into a testing script mnist_models/trainer/test.py to make sure the model still passes our previous checks. On line 13, you can specify which model types you would like to check. line 14 and line 15 have the number of epochs and steps per epoch respectively.\n",
    "\n",
    "Run the code below to check your models against the unit tests. If you see \"OK\" at the end when it's finished running, congrats! You've passed the tests!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-01-10 11:10:26.956391: I tensorflow/core/common_runtime/process_util.cc:146] Creating new thread pool with default inter op setting: 2. Tune using inter_op_parallelism_threads for best performance.\n",
      "2022-01-10 11:10:27.032398: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:185] None of the MLIR Optimization Passes are enabled (registered 2)\n",
      "..\n",
      "*** Building model for linear ***\n",
      "\n",
      "Epoch 1/10\n",
      "100/100 - 6s - loss: 1.3205 - accuracy: 0.6661 - val_loss: 0.7883 - val_accuracy: 0.8210\n",
      "Epoch 2/10\n",
      "100/100 - 1s - loss: 0.6711 - accuracy: 0.8396 - val_loss: 0.5605 - val_accuracy: 0.8658\n",
      "Epoch 3/10\n",
      "100/100 - 1s - loss: 0.5260 - accuracy: 0.8742 - val_loss: 0.4689 - val_accuracy: 0.8826\n",
      "Epoch 4/10\n",
      "100/100 - 2s - loss: 0.4703 - accuracy: 0.8773 - val_loss: 0.4196 - val_accuracy: 0.8934\n",
      "Epoch 5/10\n",
      "100/100 - 1s - loss: 0.4305 - accuracy: 0.8874 - val_loss: 0.3920 - val_accuracy: 0.8960\n",
      "Epoch 6/10\n",
      "100/100 - 2s - loss: 0.3896 - accuracy: 0.8963 - val_loss: 0.3653 - val_accuracy: 0.9042\n",
      "Epoch 7/10\n",
      "100/100 - 1s - loss: 0.3758 - accuracy: 0.8976 - val_loss: 0.3520 - val_accuracy: 0.9058\n",
      "Epoch 8/10\n",
      "100/100 - 1s - loss: 0.3714 - accuracy: 0.8994 - val_loss: 0.3408 - val_accuracy: 0.9079\n",
      "Epoch 9/10\n",
      "100/100 - 1s - loss: 0.3443 - accuracy: 0.9077 - val_loss: 0.3315 - val_accuracy: 0.9095\n",
      "Epoch 10/10\n",
      "100/100 - 1s - loss: 0.3574 - accuracy: 0.9003 - val_loss: 0.3239 - val_accuracy: 0.9098\n",
      "\n",
      "*** Building model for dnn ***\n",
      "\n",
      "Epoch 1/10\n",
      "100/100 - 5s - loss: 0.5966 - accuracy: 0.8317 - val_loss: 0.2996 - val_accuracy: 0.9136\n",
      "Epoch 2/10\n",
      "100/100 - 2s - loss: 0.2474 - accuracy: 0.9297 - val_loss: 0.2354 - val_accuracy: 0.9350\n",
      "Epoch 3/10\n",
      "100/100 - 1s - loss: 0.2067 - accuracy: 0.9414 - val_loss: 0.1759 - val_accuracy: 0.9458\n",
      "Epoch 4/10\n",
      "100/100 - 2s - loss: 0.1874 - accuracy: 0.9459 - val_loss: 0.1452 - val_accuracy: 0.9570\n",
      "Epoch 5/10\n",
      "100/100 - 1s - loss: 0.1526 - accuracy: 0.9572 - val_loss: 0.1289 - val_accuracy: 0.9593\n",
      "Epoch 6/10\n",
      "100/100 - 1s - loss: 0.1382 - accuracy: 0.9630 - val_loss: 0.1239 - val_accuracy: 0.9606\n",
      "Epoch 7/10\n",
      "100/100 - 1s - loss: 0.1153 - accuracy: 0.9652 - val_loss: 0.1106 - val_accuracy: 0.9654\n",
      "Epoch 8/10\n",
      "100/100 - 1s - loss: 0.1049 - accuracy: 0.9679 - val_loss: 0.1072 - val_accuracy: 0.9673\n",
      "Epoch 9/10\n",
      "100/100 - 2s - loss: 0.0929 - accuracy: 0.9735 - val_loss: 0.0902 - val_accuracy: 0.9715\n",
      "Epoch 10/10\n",
      "100/100 - 1s - loss: 0.0893 - accuracy: 0.9726 - val_loss: 0.1087 - val_accuracy: 0.9655\n",
      "\n",
      "*** Building model for dnn_dropout ***\n",
      "\n",
      "Epoch 1/10\n",
      "100/100 - 5s - loss: 0.6451 - accuracy: 0.8088 - val_loss: 0.2746 - val_accuracy: 0.9172\n",
      "Epoch 2/10\n",
      "100/100 - 1s - loss: 0.3084 - accuracy: 0.9087 - val_loss: 0.1987 - val_accuracy: 0.9400\n",
      "Epoch 3/10\n",
      "100/100 - 1s - loss: 0.2309 - accuracy: 0.9341 - val_loss: 0.1780 - val_accuracy: 0.9460\n",
      "Epoch 4/10\n",
      "100/100 - 1s - loss: 0.2006 - accuracy: 0.9424 - val_loss: 0.1478 - val_accuracy: 0.9536\n",
      "Epoch 5/10\n",
      "100/100 - 1s - loss: 0.1570 - accuracy: 0.9551 - val_loss: 0.1440 - val_accuracy: 0.9547\n",
      "Epoch 6/10\n",
      "100/100 - 2s - loss: 0.1646 - accuracy: 0.9543 - val_loss: 0.1186 - val_accuracy: 0.9627\n",
      "Epoch 7/10\n",
      "100/100 - 2s - loss: 0.1321 - accuracy: 0.9601 - val_loss: 0.1231 - val_accuracy: 0.9601\n",
      "Epoch 8/10\n",
      "100/100 - 1s - loss: 0.1169 - accuracy: 0.9649 - val_loss: 0.1037 - val_accuracy: 0.9680\n",
      "Epoch 9/10\n",
      "100/100 - 1s - loss: 0.1121 - accuracy: 0.9676 - val_loss: 0.0924 - val_accuracy: 0.9699\n",
      "Epoch 10/10\n",
      "100/100 - 2s - loss: 0.1023 - accuracy: 0.9678 - val_loss: 0.0996 - val_accuracy: 0.9689\n",
      "\n",
      "*** Building model for cnn ***\n",
      "\n",
      "Epoch 1/10\n",
      "100/100 - 10s - loss: 0.6706 - accuracy: 0.7978 - val_loss: 0.2021 - val_accuracy: 0.9384\n",
      "Epoch 2/10\n",
      "100/100 - 8s - loss: 0.2099 - accuracy: 0.9420 - val_loss: 0.1285 - val_accuracy: 0.9602\n",
      "Epoch 3/10\n",
      "100/100 - 7s - loss: 0.1380 - accuracy: 0.9606 - val_loss: 0.0908 - val_accuracy: 0.9729\n",
      "Epoch 4/10\n",
      "100/100 - 8s - loss: 0.1041 - accuracy: 0.9683 - val_loss: 0.0600 - val_accuracy: 0.9795\n",
      "Epoch 5/10\n",
      "100/100 - 8s - loss: 0.0973 - accuracy: 0.9696 - val_loss: 0.0646 - val_accuracy: 0.9797\n",
      "Epoch 6/10\n",
      "100/100 - 8s - loss: 0.0826 - accuracy: 0.9762 - val_loss: 0.0518 - val_accuracy: 0.9834\n",
      "Epoch 7/10\n",
      "100/100 - 8s - loss: 0.0606 - accuracy: 0.9815 - val_loss: 0.0402 - val_accuracy: 0.9866\n",
      "Epoch 8/10\n",
      "100/100 - 8s - loss: 0.0622 - accuracy: 0.9821 - val_loss: 0.0387 - val_accuracy: 0.9873\n",
      "Epoch 9/10\n",
      "100/100 - 7s - loss: 0.0516 - accuracy: 0.9854 - val_loss: 0.0487 - val_accuracy: 0.9840\n",
      "Epoch 10/10\n",
      "100/100 - 8s - loss: 0.0592 - accuracy: 0.9827 - val_loss: 0.0379 - val_accuracy: 0.9874\n",
      "...\n",
      "----------------------------------------------------------------------\n",
      "Ran 5 tests in 138.340s\n",
      "\n",
      "OK\n"
     ]
    }
   ],
   "source": [
    "!python3 -m mnist_models.trainer.test"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now you know that your models are working as expected. Now ,you can run it on Google Cloud within Vertex AI. You can run it as a python module locally first using the command line.\n",
    "\n",
    "The below cell transfers some of your variables to the command line and creates a job directory including a timestamp."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "current_time = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
    "model_type = 'cnn'\n",
    "\n",
    "os.environ[\"MODEL_TYPE\"] = model_type\n",
    "os.environ[\"JOB_DIR\"] = \"mnist_models/models/{}_{}/\".format(\n",
    "    model_type, current_time)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The cell below runs the local version of the code. The epochs and steps_per_epoch flag can be changed to run for longer or shorter, as defined in your mnist_models/trainer/task.py file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/5\n",
      "50/50 - 9s - loss: 1.0487 - accuracy: 0.6644 - val_loss: 0.3334 - val_accuracy: 0.8990\n",
      "Epoch 2/5\n",
      "50/50 - 5s - loss: 0.3760 - accuracy: 0.8886 - val_loss: 0.2216 - val_accuracy: 0.9332\n",
      "Epoch 3/5\n",
      "50/50 - 4s - loss: 0.2188 - accuracy: 0.9376 - val_loss: 0.1374 - val_accuracy: 0.9590\n",
      "Epoch 4/5\n",
      "50/50 - 5s - loss: 0.1550 - accuracy: 0.9534 - val_loss: 0.1135 - val_accuracy: 0.9678\n",
      "Epoch 5/5\n",
      "50/50 - 5s - loss: 0.1457 - accuracy: 0.9554 - val_loss: 0.0957 - val_accuracy: 0.9680\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2022-01-10 11:13:38.108448: I tensorflow/core/common_runtime/process_util.cc:146] Creating new thread pool with default inter op setting: 2. Tune using inter_op_parallelism_threads for best performance.\n",
      "2022-01-10 11:13:38.580137: I tensorflow/core/profiler/lib/profiler_session.cc:131] Profiler session initializing.\n",
      "2022-01-10 11:13:38.580185: I tensorflow/core/profiler/lib/profiler_session.cc:146] Profiler session started.\n",
      "2022-01-10 11:13:38.581203: I tensorflow/core/profiler/lib/profiler_session.cc:164] Profiler session tear down.\n",
      "2022-01-10 11:13:39.204819: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:185] None of the MLIR Optimization Passes are enabled (registered 2)\n",
      "2022-01-10 11:13:42.670285: I tensorflow/core/profiler/lib/profiler_session.cc:131] Profiler session initializing.\n",
      "2022-01-10 11:13:42.670331: I tensorflow/core/profiler/lib/profiler_session.cc:146] Profiler session started.\n",
      "2022-01-10 11:13:42.731554: I tensorflow/core/profiler/lib/profiler_session.cc:66] Profiler session collecting data.\n",
      "2022-01-10 11:13:42.741425: I tensorflow/core/profiler/lib/profiler_session.cc:164] Profiler session tear down.\n",
      "2022-01-10 11:13:42.758424: I tensorflow/core/profiler/rpc/client/save_profile.cc:136] Creating directory: mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42\n",
      "\n",
      "2022-01-10 11:13:42.763094: I tensorflow/core/profiler/rpc/client/save_profile.cc:142] Dumped gzipped tool data for trace.json.gz to mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42/tensorflow-2-6-20220110-155153.trace.json.gz\n",
      "2022-01-10 11:13:42.794321: I tensorflow/core/profiler/rpc/client/save_profile.cc:136] Creating directory: mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42\n",
      "\n",
      "2022-01-10 11:13:42.797500: I tensorflow/core/profiler/rpc/client/save_profile.cc:142] Dumped gzipped tool data for memory_profile.json.gz to mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42/tensorflow-2-6-20220110-155153.memory_profile.json.gz\n",
      "2022-01-10 11:13:42.799522: I tensorflow/core/profiler/rpc/client/capture_profile.cc:251] Creating directory: mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42\n",
      "Dumped tool data for xplane.pb to mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42/tensorflow-2-6-20220110-155153.xplane.pb\n",
      "Dumped tool data for overview_page.pb to mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42/tensorflow-2-6-20220110-155153.overview_page.pb\n",
      "Dumped tool data for input_pipeline.pb to mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42/tensorflow-2-6-20220110-155153.input_pipeline.pb\n",
      "Dumped tool data for tensorflow_stats.pb to mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42/tensorflow-2-6-20220110-155153.tensorflow_stats.pb\n",
      "Dumped tool data for kernel_stats.pb to mnist_models/models/cnn_20220110_111330/train/plugins/profile/2022_01_10_11_13_42/tensorflow-2-6-20220110-155153.kernel_stats.pb\n",
      "\n",
      "2022-01-10 11:14:20.832201: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "python3 -m mnist_models.trainer.task \\\n",
    "    --job-dir=$JOB_DIR \\\n",
    "    --epochs=5 \\\n",
    "    --steps_per_epoch=50 \\\n",
    "    --model_type=$MODEL_TYPE"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Training on the cloud\n",
    "\n",
    "For this model, you use a Tensorflow pre-built container on Vertex AI, as you do not have any particular additional prerequisites. You use setuptools for this, and store the created source distribution on Cloud Storage by using “**gcloud storage cp**”."   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing mnist_models/setup.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile mnist_models/setup.py\n",
    "from setuptools import find_packages\n",
    "from setuptools import setup\n",
    "\n",
    "setup(\n",
    "    name='mnist_trainer',\n",
    "    version='0.1',\n",
    "    packages=find_packages(),\n",
    "    include_package_data=True,\n",
    "    description='MNIST model training application.'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "running sdist\n",
      "running egg_info\n",
      "creating mnist_trainer.egg-info\n",
      "writing mnist_trainer.egg-info/PKG-INFO\n",
      "writing dependency_links to mnist_trainer.egg-info/dependency_links.txt\n",
      "writing top-level names to mnist_trainer.egg-info/top_level.txt\n",
      "writing manifest file 'mnist_trainer.egg-info/SOURCES.txt'\n",
      "reading manifest file 'mnist_trainer.egg-info/SOURCES.txt'\n",
      "writing manifest file 'mnist_trainer.egg-info/SOURCES.txt'\n",
      "running check\n",
      "creating mnist_trainer-0.1\n",
      "creating mnist_trainer-0.1/mnist_trainer.egg-info\n",
      "creating mnist_trainer-0.1/trainer\n",
      "copying files to mnist_trainer-0.1...\n",
      "copying setup.py -> mnist_trainer-0.1\n",
      "copying mnist_trainer.egg-info/PKG-INFO -> mnist_trainer-0.1/mnist_trainer.egg-info\n",
      "copying mnist_trainer.egg-info/SOURCES.txt -> mnist_trainer-0.1/mnist_trainer.egg-info\n",
      "copying mnist_trainer.egg-info/dependency_links.txt -> mnist_trainer-0.1/mnist_trainer.egg-info\n",
      "copying mnist_trainer.egg-info/top_level.txt -> mnist_trainer-0.1/mnist_trainer.egg-info\n",
      "copying trainer/__init__.py -> mnist_trainer-0.1/trainer\n",
      "copying trainer/model.py -> mnist_trainer-0.1/trainer\n",
      "copying trainer/task.py -> mnist_trainer-0.1/trainer\n",
      "copying trainer/test.py -> mnist_trainer-0.1/trainer\n",
      "copying trainer/util.py -> mnist_trainer-0.1/trainer\n",
      "Writing mnist_trainer-0.1/setup.cfg\n",
      "creating dist\n",
      "Creating tar archive\n",
      "removing 'mnist_trainer-0.1' (and everything under it)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md\n",
      "\n",
      "warning: check: missing required meta-data: url\n",
      "\n",
      "warning: check: missing meta-data: either (author and author_email) or (maintainer and maintainer_email) must be supplied\n",
      "\n",
      "Copying file://mnist_models/dist/mnist_trainer-0.1.tar.gz [Content-Type=application/x-tar]...\n",
      "/ [1 files][  3.2 KiB/  3.2 KiB]                                                \n",
      "Operation completed over 1 objects/3.2 KiB.                                      \n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "cd mnist_models\n",
    "python ./setup.py sdist --formats=gztar\n",
    "cd ..\n",
    "gcloud storage cp mnist_models/dist/mnist_trainer-0.1.tar.gz gs://${BUCKET}/mnist/"   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then, you can start the Vertex AI Custom Job using the pre-built container. You can pass your source distribution URI using the --python-package-uris flag."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "current_time = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
    "model_type = 'cnn'\n",
    "\n",
    "os.environ[\"MODEL_TYPE\"] = model_type\n",
    "os.environ[\"JOB_DIR\"] = \"gs://{}/mnist_{}_{}/\".format(\n",
    "    BUCKET, model_type, current_time)\n",
    "os.environ[\"JOB_NAME\"] = \"mnist_{}_{}\".format(\n",
    "    model_type, current_time)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After submitting the following job, view the status in __Vertex AI__ > __Training__ and select __Custom Jobs__ tab. Wait for the job to finish."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gs://qwiklabs-gcp-04-25783cd152f3/mnist_cnn_20220110_112058/ us-central1 mnist_cnn_20220110_112058\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using endpoint [https://us-central1-aiplatform.googleapis.com/]\n",
      "CustomJob [projects/867925218804/locations/us-central1/customJobs/3584008783828877312] is submitted successfully.\n",
      "\n",
      "Your job is still active. You may view the status of your job with the command\n",
      "\n",
      "  $ gcloud ai custom-jobs describe projects/867925218804/locations/us-central1/customJobs/3584008783828877312\n",
      "\n",
      "or continue streaming the logs with the command\n",
      "\n",
      "  $ gcloud ai custom-jobs stream-logs projects/867925218804/locations/us-central1/customJobs/3584008783828877312\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "echo $JOB_DIR $REGION $JOB_NAME\n",
    "\n",
    "PYTHON_PACKAGE_URIS=gs://${BUCKET}/mnist/mnist_trainer-0.1.tar.gz\n",
    "MACHINE_TYPE=n1-standard-4\n",
    "REPLICA_COUNT=1\n",
    "PYTHON_PACKAGE_EXECUTOR_IMAGE_URI=\"us-docker.pkg.dev/vertex-ai/training/tf-cpu.2-3:latest\"\n",
    "PYTHON_MODULE=trainer.task\n",
    "    \n",
    "WORKER_POOL_SPEC=\"machine-type=$MACHINE_TYPE,\\\n",
    "replica-count=$REPLICA_COUNT,\\\n",
    "executor-image-uri=$PYTHON_PACKAGE_EXECUTOR_IMAGE_URI,\\\n",
    "python-module=$PYTHON_MODULE\"\n",
    "\n",
    "gcloud ai custom-jobs create \\\n",
    "  --region=${REGION} \\\n",
    "  --display-name=$JOB_NAME \\\n",
    "  --python-package-uris=$PYTHON_PACKAGE_URIS \\\n",
    "  --worker-pool-spec=$WORKER_POOL_SPEC \\\n",
    "  --args=\"--job-dir=$JOB_DIR,--model_type=$MODEL_TYPE\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gs://qwiklabs-gcp-04-25783cd152f3/mnist_cnn_20220110_112058/keras_export\n",
      "gs://qwiklabs-gcp-04-25783cd152f3/mnist_cnn_20220110_112058/keras_export/\n",
      "gs://qwiklabs-gcp-04-25783cd152f3/mnist_cnn_20220110_112058/keras_export/saved_model.pb\n",
      "gs://qwiklabs-gcp-04-25783cd152f3/mnist_cnn_20220110_112058/keras_export/assets/\n",
      "gs://qwiklabs-gcp-04-25783cd152f3/mnist_cnn_20220110_112058/keras_export/variables/\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "SAVEDMODEL_DIR=${JOB_DIR}keras_export\n",
    "echo $SAVEDMODEL_DIR\n",
    "gcloud storage ls $SAVEDMODEL_DIR"   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploying and predicting with model\n",
    "\n",
    "Once you have a model you're proud of, let's deploy it! All you need to do is to upload the created model artifact from Cloud Storage to Vertex AI as a model, create a new endpoint, and deploy the model to the endpoint. It can take 15-20 minutes to complete. Make a note of **ENDPOINT_RESOURCENAME** for further use."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gs://qwiklabs-gcp-04-25783cd152f3/mnist_cnn_20220110_112058/keras_export\n",
      "MODEL_DISPLAYNAME=mnist_20220110_113117\n",
      "MODEL_RESOURCENAME=projects/867925218804/locations/us-central1/models/5397040785868718080\n",
      "ENDPOINT_DISPLAYNAME=mnist_endpoint_20220110_113117\n",
      "ENDPOINT_RESOURCENAME=projects/867925218804/locations/us-central1/endpoints/2075394168124866560\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using endpoint [https://us-central1-aiplatform.googleapis.com/]\n",
      "Waiting for operation [3241482393193807872]...\n",
      "...................done.\n",
      "Using endpoint [https://us-central1-aiplatform.googleapis.com/]\n",
      "Waiting for operation [4016101529101533184]...\n",
      ".....................................done.\n",
      "Created Vertex AI endpoint: projects/867925218804/locations/us-central1/endpoints/2075394168124866560.\n",
      "Using endpoint [https://us-central1-aiplatform.googleapis.com/]\n",
      "Waiting for operation [5430231812095868928]...\n",
      ".....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................done.\n",
      "Deployed a model to the endpoint 2075394168124866560. Id of the deployed model: 7191377791701483520.\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "TIMESTAMP=$(date -u +%Y%m%d_%H%M%S)\n",
    "MODEL_DISPLAYNAME=mnist_$TIMESTAMP\n",
    "ENDPOINT_DISPLAYNAME=mnist_endpoint_$TIMESTAMP\n",
    "IMAGE_URI=\"us-docker.pkg.dev/vertex-ai/prediction/tf2-cpu.2-3:latest\"\n",
    "SAVEDMODEL_DIR=${JOB_DIR}keras_export\n",
    "echo $SAVEDMODEL_DIR\n",
    "\n",
    "# Model\n",
    "MODEL_RESOURCENAME=$(gcloud ai models upload \\\n",
    "    --region=$REGION \\\n",
    "    --display-name=$MODEL_DISPLAYNAME \\\n",
    "    --container-image-uri=$IMAGE_URI \\\n",
    "    --artifact-uri=$SAVEDMODEL_DIR \\\n",
    "    --format=\"value(model)\")\n",
    "\n",
    "echo \"MODEL_DISPLAYNAME=${MODEL_DISPLAYNAME}\"\n",
    "echo \"MODEL_RESOURCENAME=${MODEL_RESOURCENAME}\"\n",
    "\n",
    "# Endpoint\n",
    "ENDPOINT_RESOURCENAME=$(gcloud ai endpoints create \\\n",
    "  --region=$REGION \\\n",
    "  --display-name=$ENDPOINT_DISPLAYNAME \\\n",
    "  --format=\"value(name)\")\n",
    "\n",
    "echo \"ENDPOINT_DISPLAYNAME=${ENDPOINT_DISPLAYNAME}\"\n",
    "echo \"ENDPOINT_RESOURCENAME=${ENDPOINT_RESOURCENAME}\"\n",
    "\n",
    "# Deployment\n",
    "DEPLOYED_MODEL_DISPLAYNAME=${MODEL_DISPLAYNAME}_deployment\n",
    "MACHINE_TYPE=n1-standard-2\n",
    "\n",
    "gcloud ai endpoints deploy-model $ENDPOINT_RESOURCENAME \\\n",
    "  --region=$REGION \\\n",
    "  --model=$MODEL_RESOURCENAME \\\n",
    "  --display-name=$DEPLOYED_MODEL_DISPLAYNAME \\\n",
    "  --machine-type=$MACHINE_TYPE \\\n",
    "  --min-replica-count=1 \\\n",
    "  --max-replica-count=1 \\\n",
    "  --traffic-split=0=100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To predict with the model, take one of the example images.\n",
    "\n",
    "Write a .json file with image data to send to a Vertex AI deployed model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import json, codecs\n",
    "import tensorflow as tf\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "HEIGHT = 28\n",
    "WIDTH = 28\n",
    "IMGNO = 12\n",
    "\n",
    "mnist = tf.keras.datasets.mnist.load_data()\n",
    "(x_train, y_train), (x_test, y_test) = mnist\n",
    "test_image = x_test[IMGNO]\n",
    "\n",
    "# TODO 4: Prepare jsondata\n",
    "jsondata = {\"instances\": [\n",
    "    test_image.reshape(HEIGHT, WIDTH, 1).tolist()\n",
    "]}\n",
    "json.dump(jsondata, codecs.open(\"test.json\", \"w\", encoding = \"utf-8\"))\n",
    "plt.imshow(test_image.reshape(HEIGHT, WIDTH));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"instances\": [[[[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [49], [180], [253], [255], [253], [169], [36], [11], [76], [9], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [5], [68], [228], [252], [252], [253], [252], [252], [160], [189], [253], [92], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [55], [252], [252], [227], [79], [69], [69], [100], [90], [236], [247], [67], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [43], [233], [252], [185], [50], [0], [0], [0], [26], [203], [252], [135], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [168], [253], [178], [37], [0], [0], [0], [0], [70], [252], [252], [63], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [155], [253], [242], [42], [0], [0], [0], [0], [5], [191], [253], [190], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [207], [252], [230], [0], [0], [0], [0], [5], [136], [252], [252], [64], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [207], [252], [230], [0], [0], [0], [32], [138], [252], [252], [227], [16], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [165], [252], [249], [207], [207], [207], [228], [253], [252], [252], [160], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [9], [179], [253], [252], [252], [252], [252], [75], [169], [252], [56], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [64], [116], [116], [74], [0], [149], [253], [215], [21], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [253], [252], [162], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [32], [253], [240], [50], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [157], [253], [164], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [43], [240], [253], [92], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [93], [253], [252], [84], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [114], [252], [209], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [207], [252], [116], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [165], [252], [116], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [93], [200], [63], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]], [[0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]]]]}"
     ]
    }
   ],
   "source": [
    "!cat test.json"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, you can send it to the prediction service. The output will have a 1 in the index of the corresponding digit it is predicting. Congrats! You've completed the lab!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0, 0, 0, 0, 0, 0, 0, 0, 0, 1]]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using endpoint [https://us-central1-prediction-aiplatform.googleapis.com/]\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "ENDPOINT_RESOURCENAME=#Insert ENDPOINT_RESOURCENAME from above\n",
    "\n",
    "gcloud ai endpoints predict $ENDPOINT_RESOURCENAME \\\n",
    "    --region=$REGION \\\n",
    "    --json-request=test.json"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright 2021 Google Inc.\n",
    "Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n",
    "http://www.apache.org/licenses/LICENSE-2.0\n",
    "Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License."
   ]
  }
 ],
 "metadata": {
  "environment": {
   "kernel": "python3",
   "name": "tf2-gpu.2-6.m87",
   "type": "gcloud",
   "uri": "gcr.io/deeplearning-platform-release/tf2-gpu.2-6:m87"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
